﻿using System;
using System.Text;
using PhoneNetTools.Utilities;

namespace PhoneNetTools.Dns.Records
{
    /// <summary>
    /// The base class implementation for resource records (including questions).
    /// </summary>
    public abstract class ResourceRecordBase
    {
        /// <summary>
        /// Gets or sets the name of the node to which this resource record pertains.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the type of the resource record.
        /// </summary>
        public QType QType
        {
            get;
            protected set;
        }

        /// <summary>
        /// Gets or sets the class of the resource record. This typically is <c>IN</c>.
        /// </summary>
        public QClass Class
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the length of the whole resource record, in bytes, when implemented in derived classes.
        /// </summary>
        public abstract int Length
        {
            get;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ResourceRecordBase"/> class and sets the class to <c>IN</c>.
        /// </summary>
        protected ResourceRecordBase()
        {
            // set default values
            Class = QClass.IN;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ResourceRecordBase"/> class using the given type, and sets the class to <c>IN</c>.
        /// </summary>
        /// <param name="type">The type.</param>
        protected ResourceRecordBase(QType type)
            : this()
        {
            QType = type;
        }

        /// <summary>
        /// Converts the content of the resource record to a raw representation.
        /// Derived classes are supposed to override this, call the base 
        /// implementation and then add their own custom data.
        /// </summary>
        /// <param name="rawData">The raw data byte array to write the content to.</param>
        /// <param name="startIndex">The start index to start writing at.</param>
        /// <returns>The new index that points to the byte directly after the last one written by this method.</returns>
        public virtual int ToRawData(byte[] rawData, int startIndex)
        {
            if (rawData == null)
            {
                throw new ArgumentNullException("rawData");
            }

            // convert string
            var parts = Name.Split('.');
            foreach (var part in parts)
            {
                rawData[startIndex] = (byte)part.Length;
                var encodedPart = Encoding.UTF8.GetBytes(part); // again: assuming ASCII
                Array.Copy(encodedPart, 0, rawData, startIndex + 1, encodedPart.Length);

                // increment
                startIndex += encodedPart.Length + 1;
            }

            // terminating char
            rawData[startIndex] = 0x0;
            startIndex++;

            // type
            var rawType = BigEndianBitConverter.GetBytes((UInt16)QType);
            Array.Copy(rawType, 0, rawData, startIndex, rawType.Length);
            startIndex += rawType.Length;

            // class
            var rawClass = BigEndianBitConverter.GetBytes((UInt16)Class);
            Array.Copy(rawClass, 0, rawData, startIndex, rawClass.Length);
            startIndex += rawClass.Length;

            // done
            return startIndex;
        }

        /// <summary>
        /// Configures the resource record from a raw representation.
        /// Derived classes are supposed to override this, call the base 
        /// implementation and then extract their own custom data,
        /// starting at the index returned by the base class implementation.
        /// </summary>
        /// <param name="rawData">The raw data byte array to extract data from.</param>
        /// <param name="startIndex">The start index to start extracting data at.</param>
        /// <returns>The new index that points to the byte directly after the last one read by this method.</returns>
        public virtual int FromRawData(byte[] rawData, int startIndex)
        {
            return startIndex;
        }
    }
}
